home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
admin
/
linuxcon.000
/
linuxcon
/
linuxconf-1.6
/
netconf
/
firewall.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-07-29
|
16KB
|
685 lines
#pragma implementation
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/icmp.h>
#include <linux/if.h>
#include <linux/ip_fw.h>
#include "netconf.h"
#include "internal.h"
#include "firewall.h"
#include "../dialog/dialog.h"
#include "../misc/misc.h"
#include "../userconf/userconf.h"
#include "../paths.h"
#include "netconf.m"
#ifndef IP_FW_POLICY_IN
/* #Specification: firewall / compatibility
linuxconf requires a kernel newer than 1.3.66 (or close) to
support firewalling. Linuxconf works with older kernels but
won't activate firewalling rules.
*/
/* These are just here so it compiles */
#define IP_FW_APPEND_IN 0
#define IP_FW_APPEND_OUT 0
#define IP_FW_APPEND_FWD 0
#define IP_FW_POLICY_IN 0
#define IP_FW_POLICY_OUT 0
#define IP_FW_POLICY_FWD 0
#define IP_FW_FLUSH_IN 0
#define IP_FW_FLUSH_OUT 0
#define IP_FW_FLUSH_FWD 0
#define FIREWALL_NONE
#endif
extern NETCONF_HELP_FILE help_ipfw;
static CONFIG_FILE ip_forward (PROC_NET_IP_FORWARD,help_ipfw
,CONFIGF_OPTIONNAL|CONFIGF_PROBED);
static CONFIG_FILE ip_block (PROC_NET_IP_BLOCK,help_ipfw
,CONFIGF_OPTIONNAL|CONFIGF_PROBED);
static CONFIG_FILE ip_input (PROC_NET_IP_INPUT,help_ipfw
,CONFIGF_OPTIONNAL|CONFIGF_PROBED);
static CONFIG_FILE ip_output (PROC_NET_IP_OUTPUT,help_ipfw
,CONFIGF_OPTIONNAL|CONFIGF_PROBED);
static CONFIG_FILE ip_acct (PROC_NET_IP_ACCT,help_ipfw
,CONFIGF_OPTIONNAL|CONFIGF_PROBED);
static CONFIG_FILE f_current_acct (VAR_RUN_FIREWALL_ACCT,help_ipfw
,CONFIGF_GENERATED|CONFIGF_OPTIONNAL|CONFIGF_ERASED);
static CONFIG_FILE f_current_block (VAR_RUN_FIREWALL_BLOCK,help_ipfw
,CONFIGF_GENERATED|CONFIGF_OPTIONNAL|CONFIGF_ERASED);
static CONFIG_FILE f_current_forwd (VAR_RUN_FIREWALL_FORWD,help_ipfw
,CONFIGF_GENERATED|CONFIGF_OPTIONNAL|CONFIGF_ERASED);
static CONFIG_FILE f_current_output (VAR_RUN_FIREWALL_OUTPUT,help_ipfw
,CONFIGF_GENERATED|CONFIGF_OPTIONNAL|CONFIGF_ERASED);
class IPFW_RULES: public ARRAY{
public:
char active;
private:
virtual IPFW_RULE *newrule()=0;
virtual IPFW_RULE *newrule(const char *pt)=0;
virtual int save()=0;
virtual int kernelok()=0;
virtual int enable(int doit, SSTRING *collect)=0;
public:
virtual int disable(int doit, SSTRING *collect)=0;
/*~PROTOBEG~ IPFW_RULES */
public:
void edit (void);
IPFW_RULE *getitem (int no);
protected:
void init (const char *key,
const char *key_active);
private:
void reset_nbbitmsk (void);
protected:
int savek (const char *key,
const char *key_active);
public:
int setup (int doit, SSTRING *collect);
/*~PROTOEND~ IPFW_RULES */
};
class IPFW_RULES_FORWARD: public IPFW_RULES{
/*~PROTOBEG~ IPFW_RULES_FORWARD */
public:
IPFW_RULES_FORWARD (void);
int disable (int doit, SSTRING *collect);
int enable (int doit, SSTRING *collect);
int kernelok (void);
protected:
IPFW_RULE *newrule (const char *pt);
IPFW_RULE *newrule (void);
public:
int save (void);
/*~PROTOEND~ IPFW_RULES_FORWARD */
};
class IPFW_RULES_INPUT: public IPFW_RULES{
/*~PROTOBEG~ IPFW_RULES_INPUT */
public:
IPFW_RULES_INPUT (void);
int disable (int doit, SSTRING *collect);
int enable (int doit, SSTRING *collect);
int kernelok (void);
protected:
IPFW_RULE *newrule (const char *pt);
IPFW_RULE *newrule (void);
public:
int save (void);
/*~PROTOEND~ IPFW_RULES_INPUT */
};
class IPFW_RULES_OUTPUT: public IPFW_RULES{
/*~PROTOBEG~ IPFW_RULES_OUTPUT */
public:
IPFW_RULES_OUTPUT (void);
int disable (int doit, SSTRING *collect);
int enable (int doit, SSTRING *collect);
int kernelok (void);
protected:
IPFW_RULE *newrule (const char *pt);
IPFW_RULE *newrule (void);
public:
int save (void);
/*~PROTOEND~ IPFW_RULES_OUTPUT */
};
PUBLIC IPFW_RULE *IPFW_RULES::getitem(int no)
{
return (IPFW_RULE*)ARRAY::getitem(no);
}
static char ACTIVEF[] = "activef";
static char ACTIVEB[] = "activeb";
static char ACTIVEO[] = "activeo";
//static char ACTIVEA[] = "activea";
PROTECTED void IPFW_RULES::init (const char *key, const char *key_active)
{
SSTRINGS strs;
active = linuxconf_getvalnum (FIREWALL,key_active,0);
linuxconf_getall (FIREWALL,key,strs,0);
int nb = strs.getnb();
for (int i=0; i<nb; i++){
SSTRING *s = strs.getitem(i);
add (newrule (s->get()));
}
rstmodified();
}
PUBLIC IPFW_RULES_FORWARD::IPFW_RULES_FORWARD()
{
init (FORWARD,ACTIVEF);
}
PUBLIC IPFW_RULES_INPUT::IPFW_RULES_INPUT()
{
init (BLOCK,ACTIVEB);
}
PUBLIC IPFW_RULES_OUTPUT::IPFW_RULES_OUTPUT()
{
init (OUTPUT,ACTIVEO);
}
PROTECTED int IPFW_RULES::savek(const char *key, const char *key_active)
{
int ret = -1;
if (perm_rootaccess("update firewalling configuration")){
int nb = getnb();
linuxconf_replace (FIREWALL,key_active,active);
linuxconf_removeall(FIREWALL,key);
for (int i=0; i<nb; i++){
getitem(i)->save();
}
ret = linuxconf_save();
}
return ret;
}
PUBLIC int IPFW_RULES_FORWARD::save()
{
return IPFW_RULES::savek (FORWARD,ACTIVEF);
}
PROTECTED IPFW_RULE *IPFW_RULES_FORWARD::newrule()
{
return new IPFW_RULE_FORWARD;
}
PROTECTED IPFW_RULE *IPFW_RULES_FORWARD::newrule(const char *pt)
{
return new IPFW_RULE_FORWARD (pt);
}
PUBLIC int IPFW_RULES_INPUT::save()
{
return IPFW_RULES::savek (BLOCK,ACTIVEB);
}
PROTECTED IPFW_RULE *IPFW_RULES_INPUT::newrule()
{
return new IPFW_RULE_INPUT;
}
PROTECTED IPFW_RULE *IPFW_RULES_INPUT::newrule(const char *pt)
{
return new IPFW_RULE_INPUT (pt);
}
PUBLIC int IPFW_RULES_OUTPUT::save()
{
return IPFW_RULES::savek (OUTPUT,ACTIVEO);
}
PROTECTED IPFW_RULE *IPFW_RULES_OUTPUT::newrule()
{
return new IPFW_RULE_OUTPUT;
}
PROTECTED IPFW_RULE *IPFW_RULES_OUTPUT::newrule(const char *pt)
{
return new IPFW_RULE_OUTPUT (pt);
}
/*
Edit all firewalling rules
*/
PUBLIC void IPFW_RULES::edit()
{
if (perm_rootaccess("edit firewalling rules")){
int choice = 0;
while (1){
int nb = getnb();
const char **menuopt
= (const char**)malloc(((nb*2)+1)*sizeof(char*));
int i;
int ii=0;
for (i=0; i<nb; i++){
menuopt[ii++] = " ";
char buf[100];
getitem(i)->present (buf);
menuopt[ii++] = strdup(buf);
}
menuopt[ii] = NULL;
MENU_STATUS code = xconf_menu(
MSG_U(T_EDITFIRE,"Edit firewalling rules")
,MSG_U(I_EDITFIRE
,"You are allowed to edit/add/deleted\n"
"rules for forwarding packets")
,help_ipfw
,NULL
,NULL
,NULL
,MSG_U(I_NEWRULES,"new rules")
,menuopt
,choice);
IPFW_RULE *item = NULL;
if (choice >=0 && choice < nb){
item = getitem(choice);
}
for (i=0; i<nb; i++) free ((char*)(menuopt[i*2+1]));
free ((char**)menuopt);
if (code == MENU_ESCAPE || code == MENU_QUIT){
break;
}else if (perm_rootaccess("modify firewalling rules")){
if (code == MENU_OK){
if (item != NULL){
int ok = item->edit();
if (ok >= 0){
if (ok == 1) remove_del(item);
save();
}
}
}else if (code == MENU_ADD){
IPFW_RULE *a = newrule();
if (a->edit() == 0){
add (a);
save();
}else{
delete a;
}
}
}
}
}
}
/*
Turn off completly blocking
*/
PUBLIC int IPFW_RULES_INPUT::disable(
int doit,
SSTRING *collect)
{
// The order is important as the reverse will break connectivity
// for a moment.
return ipfw_policy (doit,collect,IP_FW_POLICY_IN,IP_FW_F_ACCEPT) == -1
|| ipfw_flush(doit,collect,IP_FW_FLUSH_IN) == -1
? -1 : 0;
}
/*
Turn on blocking
*/
PUBLIC int IPFW_RULES_INPUT::enable(
int doit,
SSTRING *collect)
{
struct ip_fw bf;
ipfw_baseinit ("127.0.0.1","all"
,"127.0.0.1","255.255.255.255","",""
,"127.0.0.1","255.255.255.255","",""
,bf);
return ipfw_append (doit,collect,IP_FW_APPEND_IN,bf) == -1
|| ipfw_policy (doit,collect,IP_FW_POLICY_IN,0) == -1
? -1 : 0;
}
/*
check if the kernel do support blocking
*/
PUBLIC int IPFW_RULES_INPUT::kernelok()
{
int ret = 1;
if (!ip_block.exist() && !ip_input.exist()){
static char shutoff = 0;
if (!shutoff){
xconf_error (MSG_U(ERR_BLOCKING
,"The kernel does not support\n"
"IP_BLOCKING, reconfigure it"));
shutoff = 1;
}
ret = 0;
}
return ret;
}
/*
Turn off completly forwarding
*/
PUBLIC int IPFW_RULES_OUTPUT::disable(
int doit,
SSTRING *collect)
{
return ipfw_policy (doit,collect,IP_FW_POLICY_OUT,IP_FW_F_ACCEPT) == -1
|| ipfw_flush(doit,collect,IP_FW_FLUSH_OUT) == -1
? -1 : 0;
}
/*
Turn on forwarding firewall
*/
PUBLIC int IPFW_RULES_OUTPUT::enable(
int doit,
SSTRING *collect)
{
/* #Specification: firewall / strategy
The blocking and outputing mecanism use a positive
logic. Everything is deny at first. Each rule supplied
by the user is opening a new hole.
The firewalling code of Linux is more general than that.
It should be good enough for most firewall and simple
for users/admin too.
*/
struct ip_fw bf;
ipfw_baseinit ("127.0.0.1","all"
,"127.0.0.1","255.255.255.255","",""
,"127.0.0.1","255.255.255.255","",""
,bf);
return ipfw_append (doit,collect,IP_FW_APPEND_OUT,bf) == -1
|| ipfw_policy (doit,collect,IP_FW_POLICY_OUT,0) == -1
? -1 : 0;
}
/*
Turn off completly forwarding
*/
PUBLIC int IPFW_RULES_FORWARD::disable(
int doit,
SSTRING *collect)
{
return ipfw_policy (doit,collect,IP_FW_POLICY_FWD,IP_FW_F_ACCEPT) == -1
|| ipfw_flush(doit,collect,IP_FW_FLUSH_FWD) == -1
? -1 : 0;
}
/*
Turn on forwarding firewall
*/
PUBLIC int IPFW_RULES_FORWARD::enable(
int doit,
SSTRING *collect)
{
return ipfw_policy (doit,collect,IP_FW_POLICY_FWD,0) == -1
? -1 : 0;
}
/*
check if the kernel do support forwarding
*/
PUBLIC int IPFW_RULES_FORWARD::kernelok()
{
int ret = 1;
if (!ip_forward.exist()){
static char shutoff = 0;
if (!shutoff){
xconf_error (MSG_U(ERR_FORWARDING
,"The kernel does not support\n"
"IP_FORWARDING, reconfigure it"));
shutoff = 1;
}
ret = 0;
}
return ret;
}
/*
check if the kernel do support outputint
*/
PUBLIC int IPFW_RULES_OUTPUT::kernelok()
{
int ret = 1;
if (!ip_output.exist()){
static char shutoff = 0;
if (!shutoff){
xconf_error (MSG_U(ERR_OUTPUT
,"The kernel does not support\n"
"IP_OUTPUTING, reconfigure it"));
shutoff = 1;
}
ret = 0;
}
return ret;
}
/*
Compare by netmask. host netmask will be first, 0.0.0.0 will be last
*/
static int cmp_by_netmask_from (const ARRAY_OBJ *o1, const ARRAY_OBJ *o2)
{
IPFW_RULE *r1 = (IPFW_RULE*)o1;
IPFW_RULE *r2 = (IPFW_RULE*)o2;
return r2->nbbitmask_from() - r1->nbbitmask_from();
}
/*
Compare by netmask. host netmask will be first, 0.0.0.0 will be last
*/
static int cmp_by_netmask_to (const ARRAY_OBJ *o1, const ARRAY_OBJ *o2)
{
IPFW_RULE *r1 = (IPFW_RULE*)o1;
IPFW_RULE *r2 = (IPFW_RULE*)o2;
return r2->nbbitmask_to() - r1->nbbitmask_to();
}
PRIVATE void IPFW_RULES::reset_nbbitmsk()
{
int n = getnb();
for (int i=0; i<n; i++){
IPFW_RULE *r = getitem(i);
r->from.nbbit_msk = -1;
r->to.nbbit_msk = -1;
}
}
/*
Apply the firewalling rules
*/
PUBLIC int IPFW_RULES::setup(
int doit,
SSTRING *collect)
{
int ret = 0;
/* #Specification: netconf / firewalling / logic
The firewalling strategy is this. When activating
one rule set (blocking, forwarding), you are closing
the door (policy deny). Each rule you enter open
a door. This is less general than using ipfw directly
but much simpler and enough for most usage.
Comments are more than welcome about this.
While setting the rules, we set the policy to accept.
We set it to deny at the end of the rule sequence. This
prevent some interruption in the networking while
installing the rules. If we would have start by denying
everything, we could have add all kind of network timeout
while trying to set the rules. Quite often, setting rules
involve DNS access.
*/
if (active){
if (!kernelok()
|| disable(doit,collect)==-1){
ret = -1;
}else{
ret = 0;
reset_nbbitmsk();
sort (cmp_by_netmask_from);
int nbr = getnb();
int i;
for (i=0; i<nbr; i++){
IPFW_RULE *r = getitem(i);
ret |= r->setup_left(doit,collect);
}
sort (cmp_by_netmask_to);
for (i=0; i<nbr; i++){
IPFW_RULE *r = getitem(i);
ret |= r->setup_right(doit,collect);
}
ret = enable (doit,collect);
}
}
return ret;
}
#ifndef FIREWALL_NONE
static int firewall_setup (
IPFW_RULES &rules,
CONFIG_FILE &f_current)
{
int ret = 0;
SSTRING collect;
if (rules.setup(0,&collect) != -1){
SSTRING current;
/* #Specification: firewalling / update needed
Probing the firewalling information to find
out if the current configuration is already
configured in the kernel is not easy. A trick
is used here. All the command generated to
configured the kernel are saved in the files
/var/run/firewall.{acct,block,forwd}.
When probing the firewall setup, we generate
the command in a string and compare that string
with the content of the file. If there is
any mismatch, the complete firewalling sequence
is reprogrammend in the kernel and the string
is saved in /var/run/firewall.state.
*/
FILE *fin = f_current.fopen ("r");
if (fin != NULL){
char buf[1000];
while (fgets(buf,sizeof(buf)-1,fin)!=NULL){
current.append (buf);
}
fclose (fin);
}
if (current.cmp(collect)!=0){
if(collect.is_empty()){
// No rules needed but the firewall must be desactivated
rules.disable(0,&collect);
if (!simul_ison()){
f_current.unlink();
rules.disable(1,NULL);
}
}else{
if (!simul_ison()){
ret = rules.setup(1,NULL);
if (ret != -1){
FILE *fout = f_current.fopen ("w");
if (fout != NULL){
fputs (collect.get(),fout);
fclose (fout);
}
}
}
}
net_prtlog ("%s",collect.get());
}
}
return ret;
}
#endif
/*
Install the firewalling rules in the kernel.
Return -1 if any errors
*/
int firewall_setup()
{
int ret = -1;
IPFW_RULES_FORWARD frules;
IPFW_RULES_INPUT brules;
IPFW_RULES_OUTPUT orules;
if (frules.active || brules.active || orules.active){
#ifdef FIREWALL_NONE
xconf_error (MSG_U(E_FWOLDKERN
,"Linuxconf was compiled on an old kernel and does not\n"
"support firewalling properly. No rule will be activated."));
#else
if (perm_rootaccess("activating the firewalling configuration")){
if (ipfw_open() != -1
&& firewall_setup (frules,f_current_forwd) != -1
&& firewall_setup (brules,f_current_block) != -1
&& firewall_setup (orules,f_current_output) != -1){
ipfw_close();
ret = 0;
}
}
#endif
}
return ret;
}
/*
Reset the firewalling rules in the kernel.
Return -1 if any errors
*/
int firewall_reset()
{
int ret = -1;
if (perm_rootaccess("disabling the firewalling configuration")){
IPFW_RULES_FORWARD frules;
IPFW_RULES_INPUT brules;
IPFW_RULES_OUTPUT orules;
f_current_forwd.unlink();
f_current_block.unlink();
f_current_output.unlink();
if (ipfw_open() != -1
&& frules.disable(1,NULL) != -1
&& brules.disable(1,NULL) != -1
&& orules.disable(1,NULL) != -1){
ipfw_close();
ret = 0;
}
}
return ret;
}
/*
Edit the default configuration of the firewall
*/
void firewall_editc()
{
IPFW_RULES_FORWARD frules;
IPFW_RULES_INPUT brules;
IPFW_RULES_OUTPUT orules;
DIALOG dia;
dia.newf_chk (MSG_U(F_BRULES,"Inputing rules"),brules.active,"are active");
dia.newf_chk (MSG_U(F_FRULES,"forwarding rules"),frules.active,"are active");
dia.newf_chk (MSG_U(F_ORULES,"outputing rules"),orules.active,"are active");
if (dia.edit("Global control of the firewalling/accounting"
,"You are allowed to disable/enable all rule sets\n"
"without deleting them. Beware that activating\n"
"an empty rule set is closing all doors!!!"
,help_ipfw.getpath()
,0)==MENU_ACCEPT){
brules.save();
frules.save();
orules.save();
}
}
/*
Edit forwarding rules
*/
void firewall_editf()
{
IPFW_RULES_FORWARD frules;
frules.edit();
}
/*
Edit blocking rules
*/
void firewall_editb()
{
IPFW_RULES_INPUT brules;
brules.edit();
}
/*
Edit outputing rules
*/
void firewall_edito()
{
IPFW_RULES_OUTPUT brules;
brules.edit();
}
/*
Edit accounting rules
*/
void firewall_edita()
{
xconf_notice ("Sorry, not implemented yet!");
// IPFW_RULES_ACCT arules;
// arules.edit();
}